#ifndef _H_LIBLUI_IN_
#define _H_LIBLUI_IN_

#include "libLUI.h"

#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>

char *strcasestr(const char *haystack, const char *needle);

int wcswidth(const wchar_t *s, size_t n);

/*
 * macro
 */

#define MAX_HEIGHT_MOUSE_CURSOR		64

/*
 * Util
 */

void TrimRight( char *str );

int GetDeviceName( char *keyword , char *device_name , int device_name_bufsize );

int SetDispMode( int fd , int option );

/*
 * keyboard
 */

int OpenKeyboardDevice( char *p_path_device_name , int path_device_name_bufsize );
void CloseKeyboardDevice( int fd );

int ConvertRawKeycode( signed char *p_shift_flag , signed char *p_ctrl_flag , signed char *p_alt_flag , uint16_t raw_code , unsigned char is_pressed );

/*
 * mouse
 */

int OpenMouseDevice( char *p_path_device_name , int path_device_name_bufsize );
void CloseMouseDevice( int fd );

/*
 * Event
 */

struct LUIEvent *LUIFetchMessage( struct LUIWindowsManager *windows_manager );
int LUIProcessMessage( struct LUIEvent *event );

/*
 * Mouse Cursor
 */

typedef void funcLUIDrawMouseCursor( struct LUIWindowsManager *windows_manager );

int LUILoadAllMouseCursors( struct LUIWindowsManager *windows_manager , char *pathfilename );

#define LUIMOUSECURSOR_TYPE_NORMAL		0
#define LUIMOUSECURSOR_TYPE_HORIZONTAL_RESIZING	1
#define LUIMOUSECURSOR_TYPE_VERTICAL_RESIZING	2
#define LUIMOUSECURSOR_TYPE_MOVING		3

/*
 * LUIControl
 */

#define LUICONTROL_TYPE_BUTTON			10
#define LUICONTROL_TYPE_LABEL			20

struct LUIControl
{
	struct LUIWindow		*window ;
	
	int				control_type ;
	void				*data ;
	
	struct LUIRectangle		control_relative_rect ;
	struct RBGA			control_bright_edge_color ;
	struct RBGA			control_dark_edge_color ;
	struct RBGA			control_bg_color ;
	struct RBGA			control_font_color ;
	
	int				styles ;
	int				properties ;
	
	funcLUIDrawControlProc		*pfuncLUIDrawControlProc ;
	funcLUIControlEventProc		*pfuncLUIControlEventProc ;
	
	struct list_head		this_listnode ;
} ;

/*
 * LUIWindow
 */

#define LUIWINDOW_BORDER_WITDH_DEFAULT		1
#define LUIWINDOW_TITLEBAR_HEIGHT_DEFAULT	24
#define LUIWINDOW_TITLEBAR_BUTTONBOX_WIDTH	20
#define LUIWINDOW_TITLEBAR_BUTTONBOX_HEIGHT	20
#define LUIWINDOW_TITLEBAR_BUTTONBOX_MARGIN_TOP	2

#define LUIWINDOW_MIN_WIDTH			100
#define LUIWINDOW_MIN_HEIGHT			(1+LUIWINDOW_TITLEBAR_HEIGHT_DEFAULT)

struct LUIWindow
{
	struct LUIWindowsManager	*windows_manager ;
	
	struct LUIControl		*taskbar_button ;
	
	struct LUIRectangle		window_rect ;
	struct LUIRectangle		restore_window_rect ;
	int				styles ;
	int				properties ;
	
	funcLUIDrawWindowProc		*pfuncLUIDrawWindowProc ;
	funcLUIWindowEventProc		*pfuncLUIWindowEventProc ;
	
	int				border_width ;
	struct RBGA			window_bright_edge_color ;
	struct RBGA			window_dark_edge_color ;
	
	struct LUIRectangle		titlebar_rect ;
	struct RBGA			titlebar_inactive_bg_color ;
	struct RBGA			titlebar_inactive_font_color ;
	struct RBGA			titlebar_bg_color ;
	struct RBGA			titlebar_font_color ;
	wchar_t				*titlebar_text ;
	int				window_titlebar_font_size ;
	int				titlebar_actual_width ;
	
	struct LUIControl		*quit_buttonbox ;
	struct LUIControl		*maximize_buttonbox ;
	struct LUIControl		*minimize_buttonbox ;
	
	struct RBGA			bg_color ;
	struct RBGA			font_color ;
	int				font_size ;
	
	int				moving_or_resizing_status ;
	
	struct LUIControl		controls_list ;
	struct LUIControl		*p_focus_control ;
	
	struct LUIWindow		*p_parent_window ;
	
	int				return_code ;
	
	struct list_head		this_listnode ;
} ;

/*
 * LUIWindowsManager
 */

#define LUITASKBAR_HEIGHT				25
#define LUITASKBAR_BUTTON_WIDTH				120
#define LUITASKBAR_BUTTON_HEIGHT			22

#define LUIWINDOW_MOVING_OR_RESIZING_STATUS_NONE	0
#define LUIWINDOW_MOVING_OR_RESIZING_STATUS_LEFT	1
#define LUIWINDOW_MOVING_OR_RESIZING_STATUS_RIGHT	2
#define LUIWINDOW_MOVING_OR_RESIZING_STATUS_TOP		3
#define LUIWINDOW_MOVING_OR_RESIZING_STATUS_BUTTOM	4
#define LUIWINDOW_MOVING_OR_RESIZING_STATUS_MOVE	11

#define LUIWINDOW_RESIZE_CAPTURE_WIDTH			3

struct LUIWindowsManager
{
	int				keyboard_fd ;
	int				mouse_fd ;
	
	int				read_fds ;
	int				max_fd ;
	
	signed char			shift_flag ;
	signed char			ctrl_flag ;
	signed char			alt_flag ;
	
	int				screen_fd ;
	struct fb_fix_screeninfo	screen_fix_info ;
	struct fb_var_screeninfo	screen_var_info ;
	struct LUIRectangle		screen_rect ;
	int				screen_bytes_per_pixel ;
	int				screen_size ;
	char				*screen_base ;
	int				screen_buffer_index ;
	int				refresh_screen_flag ;
	
	struct BitmapContext		*wallpaper_context ;
	struct LUIRectangle		wallpaper_rect ;
	
	funcLUIDrawMouseCursor		*pfuncMouseCursor[LUIMOUSECURSOR_TYPE_MOVING+1] ;
	int				current_mouse_cursor_type ;
	funcLUIDrawMouseCursor		*pfuncCurrentMouseCursor ;
	int				cursor_x ;
	int				cursor_y ;
	
	FT_Library			ft_library ;
	FT_Face				ft_face ;
	int				margin_left ;
	int				margin_right ;
	int				margin_top ;
	int				margin_bottom ;
	int				margin_space ;
	
	struct LUIWindow		windows_list ;
	int				windows_count ;
	
	struct LUIWindow		*p_focus_window ;
	int				moving_or_resizing_status ;
	
	struct LUIWindow		*p_modal_sub_window ;
	
	struct RBGA			window_bright_edge_color_default ;
	struct RBGA			window_dark_edge_color_default ;
	struct RBGA			window_titlebar_inactive_bg_color_default ;
	struct RBGA			window_titlebar_inactive_font_color_default ;
	struct RBGA			window_titlebar_bg_color_default ;
	struct RBGA			window_titlebar_font_color_default ;
	int				window_titlebar_font_size_default ;
	struct RBGA			window_bg_color_default ;
	struct RBGA			window_font_color_default ;
	int				window_font_size_default ;
	
	struct RBGA			control_bright_edge_color_default ;
	struct RBGA			control_dark_edge_color_default ;
	struct RBGA			control_bg_color_default ;
	struct RBGA			control_font_color_default ;
	
	struct LUIWindow		*taskbar ;
	struct RBGA			taskbar_bg_color_default ;
	struct RBGA			taskbar_font_color_default ;
	struct RBGA			taskbar_button_bg_color_default ;
	struct RBGA			taskbar_button_font_color_default ;
	
	struct LUIEvent			events_list ;
} ;

void LUIShowAllWindows( struct LUIWindowsManager *windows_manager );

void LUISetRefreshScreenFlag( struct LUIWindowsManager *windows_manager );
void LUIUnsetRefreshScreenFlag( struct LUIWindowsManager *windows_manager );
int LUIIsSetRefreshScreenFlag( struct LUIWindowsManager *windows_manager );

#define GET_SCREENBUFFER_ADDR(_p_env_,_x_,_y_)			( (_p_env_)->screen_base + ((_p_env_)->screen_buffer_index*(_p_env_)->screen_rect.height+(_y_))*(_p_env_)->screen_fix_info.line_length + (_x_)*sizeof(struct RBGA) )
#define GET_SCREENBUFFER_ADDR_WITH_CURSOR(_p_env_,_x_,_y_)	GET_SCREENBUFFER_ADDR( (_x_)+(_p_env_)->cursor_x,(_y_)+(_p_env_)->cursor_y )

#define DRAW_POINT(_p_env_,_visualize_rect_,_x_,_y_,_p_rbga_) \
	if( BETWEEN_IN_INT((_visualize_rect_).left,(_x_),(_visualize_rect_).left+(_visualize_rect_).width-1) \
		&& BETWEEN_IN_INT((_visualize_rect_).top,(_y_),(_visualize_rect_).top+(_visualize_rect_).height-1) \
		&& BETWEEN_IN_INT((_p_env_)->screen_rect.left,(_x_),(_p_env_)->screen_rect.left+(_p_env_)->screen_rect.width-1) \
		&& BETWEEN_IN_INT((_p_env_)->screen_rect.top,(_y_),(_p_env_)->screen_rect.top+(_p_env_)->screen_rect.height-1) ) \
	{ \
		char	*p = GET_SCREENBUFFER_ADDR((_p_env_),(_x_),(_y_)) ; \
		if( (_p_rbga_)->alpha == 0 ) \
		{ \
			*(p) = (_p_rbga_)->red ; \
			*(p+1) = (_p_rbga_)->green ; \
			*(p+2) = (_p_rbga_)->blue ; \
			*(p+3) = 0 ; \
		} \
		else \
		{ \
			*(p) = (UINT32)((_p_rbga_)->red) * (100-(_p_rbga_)->alpha) / 100 + (UINT32)(*p) * (_p_rbga_)->alpha / 100 ; \
			*(p+1) = (UINT32)((_p_rbga_)->green) * (100-(_p_rbga_)->alpha) / 100 + (UINT32)(*p) * (_p_rbga_)->alpha / 100 ; \
			*(p+2) = (UINT32)((_p_rbga_)->blue) * (100-(_p_rbga_)->alpha) / 100 + (UINT32)(*p) * (_p_rbga_)->alpha / 100 ; \
			*(p+3) = 0 ; \
		} \
	} \

#define DRAW_LINE(_p_env_,_visualize_rect_,_x1_,_y1_,_x2_,_y2_,_p_rbga_) \
	{ \
		if( (_y1_) == (_y2_) ) \
		{ \
			int	_left = MIN( (_x1_) , (_x2_) ) ; \
			int	_right = MAX( (_x1_) , (_x2_) ) ; \
			int	_x ; \
			\
			for( _x = _left ; _x <= _right ; _x++ ) \
			{ \
				DRAW_POINT( (_p_env_) , (_visualize_rect_) , _x , (_y1_) , _p_rbga_ ); \
			} \
		} \
		else if( (_x1_) == (_x2_) ) \
		{ \
			int	_top = MIN( (_y1_) , (_y2_) ) ; \
			int	_bottom = MAX( (_y1_) , (_y2_) ) ; \
			int	_y ; \
			\
			for( _y = _top ; _y <= _bottom ; _y++ ) \
			{ \
				DRAW_POINT( (_p_env_) , (_visualize_rect_) , (_x1_) , _y , _p_rbga_ ); \
			} \
		} \
		else \
		{ \
			/* Bresenham algorithm */ \
			int	_dx = (_x2_) - (_x1_) ; \
			int	_dy = (_y2_) - (_y1_) ; \
			int	_abs_dx = abs(_dx) ; \
			int	_abs_dy = abs(_dy) ; \
			int	_abs_dx2 = ( _abs_dx << 1 ) ; \
			int	_abs_dy2 = ( _abs_dy << 1 ) ; \
			int	_ux = ( _dx > 0 ? 1 : -1 ) ; \
			int	_uy = ( _dy > 0 ? 1 : -1 ) ; \
			\
			if( _abs_dx > _abs_dy ) \
			{ \
				int	_e = -_abs_dx ; \
				int	_x , _y ; \
				\
				for( _x = (_x1_) , _y = (_y1_) ; _x < (_x2_) ; _x += _ux ) \
				{ \
					DRAW_POINT( (_p_env_) , (_visualize_rect_) , _x , _y , _p_rbga_ ); \
					_e += _abs_dy2 ; \
					if( _e > 0 ) \
					{ \
						_y += _uy ; \
						_e = _e - _abs_dx2 ; \
					} \
				} \
			} \
			else \
			{ \
				int	_e = -_abs_dy ; \
				int	_x , _y ; \
				\
				for( _y = (_y1_) , _x = (_x1_) ; _y < (_y2_) ; _y += _uy ) \
				{ \
					DRAW_POINT( (_p_env_) , (_visualize_rect_) , _x , _y , _p_rbga_ ); \
					_e += _abs_dx2 ; \
					if( _e > 0 ) \
					{ \
						_x += _ux ; \
						_e = _e - _abs_dy2 ; \
					} \
				} \
			} \
		} \
	} \

#define DRAW_RECTANGLE(_p_env_,_visualize_rect_,_x1_,_y1_,_x2_,_y2_,_p_rbga_border_,_p_rbga_fill_) \
	{ \
		int		_left = MIN( (_x1_) , (_x2_) ) ; \
		int		_right = MAX( (_x1_) , (_x2_) ) ; \
		int		_top = MIN( (_y1_) , (_y2_) ) ; \
		int		_bottom = MAX( (_y1_) , (_y2_) ) ; \
		int		_x , _y ; \
		struct RBGA	*_p_rbga_border = (_p_rbga_border_) ; \
		if( _p_rbga_border == NULL ) \
			_p_rbga_border = (_p_rbga_fill_) ; \
		for( _y = _top ; _y <= _bottom ; _y++ ) \
		{ \
			for( _x = _left ; _x <= _right ; _x++ ) \
			{ \
				if( _y == _top || _y == _bottom || _x == _left || _x == _right ) \
				{ \
					DRAW_POINT( (_p_env_) , (_visualize_rect_) , _x , _y , _p_rbga_border ); \
				} \
				else \
				{ \
					DRAW_POINT( (_p_env_) , (_visualize_rect_) , _x , _y , _p_rbga_fill_ ); \
				} \
			} \
		} \
	} \

#define DRAW_TEXT(_p_env_,_visualize_rect_,_draw_rect_,_text_,_font_size_,_align_mode_,_p_font_color_) \
	{ \
		int		_i , _len ; \
		FT_GlyphSlot	_ft_glyph = NULL ; \
		FT_Bitmap	*_ft_bitmap = NULL ; \
		FT_Error	_ft_error ; \
		int		_total_draw_width ; \
		int		_pen_y , _pen_x ; \
		int		_font_bitmap_y , _font_bitmap_x ; \
		int		_y_align_offset ; \
		int		_draw_y , _draw_x ; \
		\
		FT_Set_Pixel_Sizes( (_p_env_)->ft_face , (_font_size_) , (_font_size_) ); \
		\
		_len = wcslen((_text_)) ; \
		\
		_total_draw_width = 0 ; \
		for( _i = 0 ; _i < _len ; _i++ ) \
		{ \
			_ft_error = FT_Load_Char( (_p_env_)->ft_face , (_text_)[_i] , FT_LOAD_RENDER ) ; \
			if( _ft_error ) \
				break; \
			\
			_ft_error = FT_Render_Glyph( (_p_env_)->ft_face->glyph , FT_RENDER_MODE_NORMAL ) ; \
			if( _ft_error ) \
				break; \
			\
			_ft_glyph = (_p_env_)->ft_face->glyph ; \
			_ft_bitmap = & (_ft_glyph->bitmap) ; \
			\
			if( _total_draw_width > 0 ) \
				_total_draw_width += (_p_env_)->margin_space ; \
			_total_draw_width += _ft_bitmap->width ; \
		} \
		\
		_pen_y = (_draw_rect_).top ; \
		if( (_align_mode_) == LUICONTROL_STYLE_TEXTALIGN_LEFT ) \
			_pen_x = (_draw_rect_).left + (_p_env_)->margin_left ; \
		else if( (_align_mode_) == LUICONTROL_STYLE_TEXTALIGN_RIGHT ) \
			_pen_x = (_draw_rect_).left+(_draw_rect_).width - _total_draw_width - (_p_env_)->margin_right ; \
		else \
			_pen_x = (_draw_rect_).left+(_draw_rect_).width/2 - _total_draw_width/2 ; \
		for( _i = 0 ; _i < _len ; _i++ ) \
		{ \
			_ft_error = FT_Load_Char( (_p_env_)->ft_face , (_text_)[_i] , FT_LOAD_RENDER ) ; \
			if( _ft_error ) \
				break; \
			\
			_ft_error = FT_Render_Glyph( (_p_env_)->ft_face->glyph , FT_RENDER_MODE_NORMAL ) ; \
			if( _ft_error ) \
				break; \
			\
			_ft_glyph = (_p_env_)->ft_face->glyph ; \
			_ft_bitmap = & (_ft_glyph->bitmap) ; \
			\
			_y_align_offset = ( (_font_size_) - _ft_bitmap->rows ) / 2 ; \
			for( _font_bitmap_y = 0 ; _font_bitmap_y < _ft_bitmap->rows ; _font_bitmap_y++ ) \
			{ \
				for( _font_bitmap_x = 0 ; _font_bitmap_x < _ft_bitmap->width ; _font_bitmap_x++ ) \
				{ \
					if( _ft_bitmap->buffer[_font_bitmap_y*_ft_bitmap->width+_font_bitmap_x] ) \
					{ \
						_draw_y = _pen_y + (_p_env_)->margin_top + _font_bitmap_y + _y_align_offset ; \
						_draw_x = _pen_x + _font_bitmap_x ; \
						DRAW_POINT( (_p_env_) , _visualize_rect_ , _draw_x , _draw_y , (_p_font_color_) ) \
					} \
				} \
			} \
			\
			/* _pen_x += (_ft_glyph->advance.x>>6) + (_p_env_)->margin_space ; \ */ \
			_pen_x += _ft_bitmap->width + (_p_env_)->margin_space ; \
		} \
	} \

#endif

